﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using WS.EKA.Utilities;
using System.Data.Common;

namespace WS.EKA.DataAccess
{
    public class DBContext<T> : IDisposable where T : class
    {
        protected static IConfig Config { get { return ConfigFactory.CreateConfigInstance(); } }
        public DBContext()
        {
            Database.SetInitializer<DBContextBase>(null);
        }
        protected static DbContext CreateContext()
        {
            return new DBContextBase(Config.ConnectionString);
        }

        public static int Delete(Expression<Func<T, bool>> func)
        {
            using (var ctx = CreateContext())
            {
                var dbSet = ctx.Set<T>();
                dbSet.Where(func).ToList().ForEach(x => dbSet.Remove(x));
                return ctx.SaveChanges();
            }
        }
        public static void DeleteAll()
        {
            using (var ctx = CreateContext())
            {
                var dbSet = ctx.Set<T>();
                dbSet.ToList().ForEach(x => dbSet.Remove(x));
                ctx.SaveChanges();
            }
        }

        public static void Update(T entity)
        {
            using (var ctx = CreateContext())
            {
                var dbSet = ctx.Set<T>();
                dbSet.Attach(entity);
                ctx.Entry(entity).State = EntityState.Modified;
                ctx.SaveChanges();
            }
        }

        public static void Add(T entity)
        {
            using (var ctx = CreateContext())
            {
                ctx.Set<T>().Add(entity);
                ctx.SaveChanges();
            }
        }

        public static T Get(params object[] keys)
        {
            using (var ctx = CreateContext())
            {
                return ctx.Set<T>().Find(keys);
            }
        }

        public static T Get(Expression<Func<T, bool>> func, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null)
        {
            using (var ctx = CreateContext())
            {
                var entity = default(T);
                var query = ctx.Set<T>().AsQueryable();
                if (orderBy != null)
                {
                    entity = orderBy(query).FirstOrDefault(func);
                }
                else
                {
                    entity = query.FirstOrDefault(func);
                }
                return entity;
            }
        }

        public static List<T> GetByConditions(Expression<Func<T, bool>> filter)
        {
            return GetByConditions(filter, null);
        }

        public static List<T> GetByConditions(Expression<Func<T, bool>> filter, Func<IQueryable<T>, IOrderedQueryable<T>> order = null, IList<T> data = null)
        {
            IQueryable<T> query = null;
            DbContext ctx = null;
            if (data != null)
            {
                query = data.AsQueryable();
            }
            else
            {
                ctx = CreateContext();
                query = ctx.Set<T>().AsQueryable();
            }

            if (filter != null)
            {
                query = query.Where(filter);
            }
            if (order != null)
            {
                query = order(query);
            }
            var result = query.ToList();
            if (ctx != null)
            {
                ctx.Dispose();
            }
            return result;
        }

        public static List<T> GetByPage(int startIndex, int pageSize, Expression<Func<T, bool>> filter, Func<IQueryable<T>, IOrderedQueryable<T>> order, out int recordTotal)
        {
            using (var ctx = CreateContext())
            {
                IQueryable<T> query = ctx.Set<T>().AsQueryable();
                if (filter != null)
                {
                    query = query.Where(filter);
                }
                if (startIndex <= 0)
                {
                    startIndex = 1;
                }
                recordTotal = query.Count();
                if (order == null)
                {
                    throw new Exception("Orderby can not be null!");
                }
                query = order(query);
                recordTotal = query.Count();
                return query.Skip((startIndex - 1) * pageSize).Take(pageSize).ToList();
            }
        }

        public static List<T> GetByPage(IList<T> data, int startIndex, int pageSize, Expression<Func<T, bool>> filter, Func<IQueryable<T>, IOrderedQueryable<T>> order, out int recordTotal)
        {
            IQueryable<T> query = null;
            DbContext ctx = null;
            if (data == null)
            {
                ctx = CreateContext();
                query = ctx.Set<T>().AsQueryable();
            }
            else
                query = data.AsQueryable();
            if (filter != null)
            {
                query = query.Where(filter);
            }
            if (startIndex < 1)
            {
                startIndex = 1;
            }
            if (order == null)
            {
                throw new Exception("Orderby can not be null!");
            }
            query = order(query);
            recordTotal = query.Count();
            var result = query.Skip((startIndex - 1) * pageSize).Take(pageSize).ToList();
            if (ctx != null)
            {
                ctx.Dispose();
            }
            return result;
        }


        public static List<T> GetByPage<TKey>(int startIndex, int pageSize, Expression<Func<T, bool>> filter, Expression<Func<T, TKey>> order, bool desc, out int recordTotal)
        {
            using (var ctx = CreateContext())
            {
                IQueryable<T> query = ctx.Set<T>().AsQueryable();
                if (filter != null)
                {
                    query = query.Where(filter);
                }
                if (order != null)
                {
                    if (desc)
                        query = query.OrderByDescending(order);
                    else
                        query = query.OrderBy(order);
                }
                if (startIndex < 1)
                {
                    startIndex = 1;
                }
                recordTotal = query.Count();
                return query.Skip((startIndex - 1) * pageSize).Take(pageSize).ToList();
            }
        }

        public static int ExecSQL(string sql, params object[] par)
        {
            using (var ctx = CreateContext())
            {
                var result = ctx.Database.ExecuteSqlCommand(sql, par);
                return result;
            }
        }
        public static int ExecProc(DbContext context, string procName, params DbParameter[] par)
        {
            var ctx = context ?? CreateContext();
            DbConnection conn = ctx.Database.Connection;
            try
            {

                if (conn.State != ConnectionState.Open)
                    conn.Open();
                DbCommand cmd = ctx.Database.Connection.CreateCommand();
                cmd.CommandText = procName;
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddRange(par);

                return cmd.ExecuteNonQuery();
            }
            finally
            {
                conn.Close();
            }
        }
        public static List<T> Exec2List(string sql, params object[] par)
        {
            using (var ctx = CreateContext())
            {
                return ctx.Set<T>().SqlQuery(sql, par).ToList();
            }
        }

        public static IEnumerable<T> Exec3SQL<T>(string sql, params object[] par)
        {
            using (var ctx = CreateContext())
            {
                var sqlExec = ctx.Database.SqlQuery<T>(sql, par);
                return sqlExec.ToList();
            }
        }
        public void Dispose()
        {
            GC.Collect();
        }
    }
}
